import { Queryable, QueryMeta } from '~/core/dsl';
import path from 'path';
import fs from 'fs';
import { DslRule } from 'qvog-dsl';
import { tsConfig } from '~/core/Configuration/TsConfig';
import { RuleChain } from '~/lib/rule_chain';
type NativeRuleOption = [Queryable, 'on' | 'off'];
type DslRuleOption = [DslRule<Queryable> | RuleChain, 'on' | 'off'];
class RuleOptions {
  static disabled_rules: Set<string>;
  static only_run_rules: Set<string>;
  static rules_folder = tsConfig.raw.rules_folder || 'src/rules';
  static is_enable(rule_key: string) {
    return false
  }
  static is_enable_by_disables(rule_key: string) {
    return !this.disabled_rules.has(rule_key);
  }
  static is_enable_by_enables(rule_key: string) {
    return this.only_run_rules.has(rule_key);
  }
  static init() {
    RuleOptions.disabled_rules = new Set<string>(tsConfig.options.disabled_rules || [])
    RuleOptions.only_run_rules = new Set<string>(tsConfig.options.only_run_rules || []);
    RuleOptions.is_enable = RuleOptions.only_run_rules.size ? RuleOptions.is_enable_by_enables : RuleOptions.is_enable_by_disables;
  }
}
RuleOptions.init()

function isNativeRule(obj: any): obj is Queryable {
  return (
    Array.isArray(obj) &&
    obj.length === 2 &&
    obj[0] instanceof QueryMeta &&
    typeof obj[1] === 'function'
  );
}

function isDslRule(obj: any): obj is DslRule<Queryable> {
  return (
    obj &&
    typeof obj === 'function' &&
    obj.name === 'bound funcDslRule'
  );
}
async function loadRulesFromDirectory(directory: string): Promise<{ nativeRules: NativeRuleOption[], dslRules: DslRuleOption[] }> {
  const nativeRules: NativeRuleOption[] = [];
  const dslRules: DslRuleOption[] = [];

  const files = fs.readdirSync(directory);

  for (const file of files) {
    if (file === 'base_rule.ts') continue; // Skip base_rule.ts as it is not a Rule
    const filePath = path.join(directory, file);
    const stat = fs.statSync(filePath)
    if (stat.isDirectory()) {
      const rules = await loadRulesFromDirectory(filePath)
      nativeRules.push(...rules.nativeRules);
      dslRules.push(...rules.dslRules);
      continue
    }
    if (!file.endsWith('.ts') && !file.endsWith('.js')) continue;
    try {
      const module = await import(filePath);
      for (const key of Object.keys(module)) {
        let value = module[key]
        let items = null
        if (isNativeRule(value)) {
          items = nativeRules
        } else if (isDslRule(value)) {
          items = dslRules
          value = [new QueryMeta({ name: value.name }), value];
        } else if (value instanceof RuleChain) {
          items = dslRules
          value = [value.meta, value.to_rule()];
        }
        if (!items) continue;
        items.push([value, RuleOptions.is_enable(key) ? 'on' : 'off'])
      }
    } catch (error) {
      console.error(`Error loading rule from ${filePath}:`, error);
    }
  }

  return { nativeRules, dslRules };
}

export async function load_rules(rules_folder: string = null) {
  return await loadRulesFromDirectory(rules_folder ?? RuleOptions.rules_folder)
}